package com.example.plugins

import com.example.CoalMarks
import com.example.Distributors
import com.example.DistributorsToCoalMarks
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction

interface DistributorRepo {
    fun getAll(): List<DistributorDto>

    fun getByIdOrNull(distributorId: Int): DistributorDto?

    fun create(name: String, address: String)

    fun delete(distributorId: Int)
}

class ExposedDistributorRepo : DistributorRepo {
    override fun getAll(): List<DistributorDto> {
        return transaction {
            val r = queryDistributors().toList()

            return@transaction mapToDistributors(r)
        }
    }

    override fun getByIdOrNull(distributorId: Int): DistributorDto? {
        return transaction {
            val r = queryDistributors()
                .where { Distributors.id eq distributorId }
                .toList()

            return@transaction mapToDistributors(r).singleOrNull()
        }
    }

    override fun create(name: String, address: String) {
        return transaction {
            SchemaUtils.create(Distributors)
            Distributors.insert {
                it[this.name] = name
                it[this.address] = address
            }
        }
    }
    override fun delete(distributorId: Int) {
        return transaction {
            Distributors.deleteWhere { id eq distributorId }
        }
    }

    private fun queryDistributors(): Query {
        return transaction {
            DistributorsToCoalMarks
                .join(
                    Distributors, JoinType.INNER,
                    additionalConstraint = { Distributors.id eq DistributorsToCoalMarks.distributorId }
                )
                .join(
                    CoalMarks, JoinType.INNER,
                    additionalConstraint = { CoalMarks.id eq DistributorsToCoalMarks.coalMarkId }
                )
                .select(
                    Distributors.id,
                    Distributors.name,
                    Distributors.address,
                    CoalMarks.id,
                    CoalMarks.name,
                    DistributorsToCoalMarks.price,
                )
        }
    }

    private fun mapToDistributors(rows: List<ResultRow>): List<DistributorDto> {
        val distributors = mutableMapOf<Int, DistributorDto>();

        rows.forEach { row ->
            val dId = row[Distributors.id]

            if (!distributors.containsKey(dId.value)) {
                distributors[dId.value] = DistributorDto(
                    dId.value,
                    row[Distributors.name],
                    row[Distributors.address],
                )
            }

            distributors[dId.value]!!.coalItems.add(
                CoalItemDto(
                    id = row[CoalMarks.id].value,
                    name = row[CoalMarks.name],
                    price = row[DistributorsToCoalMarks.price],
                )
            )
        }

        return distributors.values.toList();
    }
}